package forecasting.converter;

/**
 * This class is used to scale a value proportionally into a provided range.
 * 
 * You can use this class by either providing a input range in the constructor or 
 * by registering each value of the input dataset and then calling transform.
 * 
 */
public class LinearTransform {

	private double mtargetMin;
	
	private double mtargetMax;
	
	private double mSetMin = Double.MAX_VALUE;
	
	private double mSetMax = Double.MIN_VALUE;
	
	/**
	 * Create a new LinearTransform object with targets. The targets indicate 
	 * the range in which the numbers will be transformed into. 
	 * 
	 * For example, providing 0f and 1f for the target minimum and maximum
	 * will result in each call to the transform method to return a number
	 * between 0f and 1f.  
	 * 
	 * Using this constructor, the input range will be discovered by registering 
	 * the values of the data set.
	 * 
	 * @param targetMin
	 * @param targetMax
	 */
	public LinearTransform(final double targetMin, final double targetMax) {
		mtargetMin = targetMin;
		mtargetMax = targetMax;
	}
	
	/**
	 * Creates a LinearTransform object with the target range and the range
	 * of the input dataset.  Using this constructor, you will not have to register
	 * each item of the dataset before calling transform.
	 * 
	 * 
	 * @param targetMin
	 * @param targetMax
	 * @param inputMinimum
	 * @param inputMaximum
	 */
	public LinearTransform(final double targetMin, final double targetMax ,
			final double inputMinimum, final double inputMaximum) {
		mtargetMin = targetMin;
		mtargetMax = targetMax;
		mSetMin = inputMinimum;
		mSetMax = inputMaximum;
	}
	
	public void register(double number) {
		if (number < mSetMin) {
			mSetMin = number;
		}
		if (number > mSetMax) {
			mSetMax = number;
		}
	}
	
	public double transform(final double number) {
		if (mSetMax < mSetMin) {	
			throw new NumberFormatException(
					"Input range is invalid. You have not set a range for your " +
					"inputs or have not registered all the values of the input dataset.");
		}
	
		// If the range does not contain any elements, then expand. This will
		// ensure errors do not occur for datasets that registered datasets
		// containing only one value can still be transformed.
		if (mSetMax == mSetMin) {	
			mSetMax++; 
			mSetMin--;
		}
		
		return mtargetMin*(1f - (number - mSetMin) / (mSetMax - mSetMin)) +  
			mtargetMax*((number - mSetMin) / (mSetMax - mSetMin));
	}	
}
